package org.lex.lang;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.lex.collection.tree.ITreeNode;
import org.lex.collection.tree.TreeNode;
import org.lex.collection.tree.TreeUtils;
import org.lex.string.ClassPrinter;
import org.lex.string.DefaultPrinter;

public class ReflectUtils {

	public static ITreeNode getHierarchy(Class<?> clz) {
		Class<?> currentClass = clz;
		Class<?> parentClass = null;

		TreeNode currentNode = new TreeNode();
		currentNode.setData(clz);
		Method[] methods = clz.getMethods();
		for (Method m : methods) {
			TreeNode n = new TreeNode();
			n.setData(m);
			currentNode.addChild(n);
		}

		TreeNode parentNode = null;
		while (Object.class != currentClass) {
			parentNode = new TreeNode();

			Class<?>[] interfaces = currentClass.getInterfaces();
			for (Class<?> i : interfaces) {
				TreeNode inode = new TreeNode();
				inode.setData(i);
				parentNode.addChild(inode);
			}

			parentNode.addChild(currentNode);

			parentClass = currentClass.getSuperclass();
			if (null == parentClass) {
				break;
			} else {
				parentNode.setData(parentClass);
				currentClass = parentClass;
				currentNode = parentNode;
			}
		}
		return parentNode;
	}

	public static String printHierarchy(Class<?> clz) {
		return TreeUtils.print(getHierarchy(clz), new DefaultPrinter<Object>() {
			@Override
			public void format(Object object, PrintWriter out) {
				if (object instanceof Class) {
					Class<?> clz = (Class<?>) object;
					ClassPrinter.getInstance().format(clz, out);
				} else {
					TreeUtils.DEFAULT_PRINTER.format(object, out);
				}
			}
		});
	}

	public static <T> Map<String, T> getFields(Object o, Class<T> clz) {
		if (null == o)
			return Collections.emptyMap();
		Map<String, T> map = new HashMap<String, T>();
		Field[] allFields = o.getClass().getDeclaredFields();
		for (Field field : allFields) {
			if (clz.isAssignableFrom(field.getType())) {
				Object v = null;
				try {
					v = field.get(o);
				} catch (Exception e) {
					v = null;
				}
				if (null != v && clz.isInstance(v))
					map.put(field.getName(), clz.cast(v));
				else
					map.put(field.getName(), null);
			}
		}
		return map;
	}
}
